home *** CD-ROM | disk | FTP | other *** search
Text File | 1988-10-31 | 12.5 KB | 346 lines | [TEXT/MPS ] |
- {------------------------------------------------------------------------------
- #
- # Apple Macintosh Developer Technical Support
- #
- # Exception handling for MPW Pascal, MacApp and MPW C
- #
- # UFailure (aka Signals) - “Exceptional code, with a few exceptions.”
- #
- # UFailure.p - Pascal source
- #
- # Copyright © 1985-1988 Apple Computer, Inc.
- # All rights reserved.
- #
- # Versions: 1.0 11/88
- #
- # Components: UFailure.p November 1, 1988
- # UFailure.h November 1, 1988
- # UFailure.inc1.p November 1, 1988
- # UFailure.a November 1, 1988
- # TestCignal.c November 1, 1988
- # TestCignal.make November 1, 1988
- # TestSignal.p November 1, 1988
- # TestSignal.make November 1, 1988
- #
- # UFailure (or Signals) is a set of exception handling routines suitable for
- # use with MacApp, MPW C, and MPW Pascal. It is a jazzed-up version of the MacApp
- # UFailure unit. There is a set of C interfaces to it as well.
- #
- ------------------------------------------------------------------------------}
-
-
- {$P}
-
- { We assume that MacApp is being used if qTrace is defined. IF not, you may use
- “-mbg off” in the makefile if you don’t want TMONic symbology (i.e. embedded names). }
- {$IFC UNDEFINED qTrace}
- {$SETC UsingMacApp := 0}
- {$IFC UNDEFINED qDebug}
- {$SETC qDebug := 0} {default to debugging code off without MacApp}
- {$ENDC qDebug}
- {$ELSEC}
- {$SETC UsingMacApp := 1}
- {$ENDC qTrace}
-
- { Conditional compile flags used by this unit when used with MacApp:
-
- qDebug True includes debugging code, false ignores debugging code.
-
- qTrace True surrounds trap patches with $D+ and $D++ (because
- the default is $D++), false skips these directives.
-
- The settings of $D, $R and $N are imported from UMAUtil.
- }
-
- UNIT UFailure;
-
- {
- Theory:
- See the “theory of operation” comments below, and the commentary in Technical
- Note #88.
-
- *** New ***
- The warnings in technote 88 about not using CatchSignal in an expression, etc. no
- longer apply. The exact state of the routine is now restored (all non-scratch
- registers are preserved). Also, a fixed-size nonrelocatable block is now used, so
- there is a limit on the depth of nested CatchSignals. This may be adjusted by
- changing the constant SigBlockSize in UFailure.a and rebuilding the unit. For most
- applications, though, the default depth of eight will be more than sufficient.
-
- Note: If you use CatchFailures to catch exceptions then you will still be required
- to call Success or FreeSignal explicitly.
-
- *** You can use either the technote mechanism or the MacApp one in your program. It
- is OK to call Signal if CatchFailures was used, and OK to call Failure if
- CatchSignal was used, i.e. the two schemes may be freely intermixed. ***
-
- What this version adds to the MacApp mechanism is two things:
- 1.
- Exception records (FailInfo) are taken from a special heap block when
- you use CatchSignal, so you don’t have to pass a FailInfo record as a
- parameter.
- 2.
- You can take advantage of stack frames and use of Signals as described
- in Technical Note #88, without being forced to make explicit calls to
- FreeSignal. This unit is fully upwards compatible with the 1986 version
- of the technote.
-
- This includes all of the MacApp calls from the original UFailure, of course.
-
- T H E O R Y O F O P E R A T I O N
-
- This unit implements the MacApp and Signal failure mechanisms.
-
- The failure mechanism is built around exception handlers. An exception
- handler is a piece of code, generally local to some other routine, that is
- called when a failure occurs and takes action to handle the failure.
- An exception handler is of the form
-
- PROCEDURE ExceptionHandler (error: OSErr; message: LONGINT);
- or
- the handler may consist of execution returning to the point of a
- CatchSignal
-
- where error is the error that caused the failure, and message identifies
- the error message that may be displayed. Consider a routine that opens
- a file, reads its contents, and closes the file. If a failure occured
- while reading the file, an exception handler would be needed to close the
- file, as the rest of the routine would not be executed. (See TestCignal
- and TestSignal for examples of how to use these calls.)
-
- References to exception handlers are kept in the FailInfo record. The
- exception handlers form a linked-list via the nextInfo field of FailInfo.
- The linked list is a stack since new exception handlers are added to the
- front of the list.
-
- New exception handlers are added to the stack with the CatchSignal
- function or CatchFailures procedure. They are removed from the stack
- automatically (CatchSignal) or via the Success procedure (if CatchFailures
- was used). In general you call CatchFailures/CatchSignal to post an exception
- handler when an error the application should handle might occur. You
- can then manually pop the last handler off the stack with FreeSignal or
- Success, if necessary. You may want to pop the handler (even if you used
- CatchSignal) after the possibility of a specific type of error no longer
- exists. Subsequent exceptions would then be passed to a previous (more
- general) handler.
-
- Any failure detected within the limits of the CatchFailures/CatchSignal call
- results in the execution of the exception handler. (Failure does
- not have to occur in the same routine as your call to CatchFailures.
- The failure may occur in any routine called after the catch but before the
- implicit/explicit pop of the handler.)
-
- When MacApp (or your code) determines that a failure has occured, it
- calls Failure or Signal. As a convenience, several procedures are provided
- to check for standard kinds of failures and call Failure if needed.
- These procedures are:
-
- FailNIL Calls Failure if its parameter is NIL.
- FailOSErr Calls Failure if its parameter is not noErr.
- FailMemError Calls Failure if MemError returns other than noErr.
- FailResError Calls Failure if ResError returns other than noErr.
-
- When the exception is raised, execution of the routine that signalled is
- terminated and the exception handler at the top of the stack is popped.
- For each routine that was called after the handler was posted
- to the stack, execution is terminated as though from an EXIT statement,
- and the exception handler is called. It generally cleans up for the
- routine in which it is nested. Upon completion the next exception handler
- is popped from the stack, repeating the process.
-
- The error causing the failure, and a message code is passed to the handler.
-
- MacApp Specifics
- For MacApp, the last exception handler on the stack is the one in
- TApplication.PollEvent. It calls TApplication.ShowError, which calls
- ErrorAlert, which decodes the message and displays an alert. Your exception
- handlers may set the message code to one more specific to your application
- by calling FailNewMessage at the end of your exception handler.
- FailNewMessage changes the message only if the current one is non-zero.
- This has the effect of allowing those exception handlers closest to the
- source of the error to set the message.
-
- One last note about exception handlers: It is possible for an exception
- handler to terminate exception processing by using a non-local GOTO to
- jump back into the routine in which the exception handler is nested. This
- is how MacApp keeps the application running when a failure occurs. The
- last exception handler on the stack, in TApplication.PollEvent, uses a
- GOTO to continue event processing.
- }
-
- INTERFACE
-
- {$IFC UsingMacApp}
- USES
- {$LOAD MacIntf.LOAD}
- MemTypes, QuickDraw, OSIntf, ToolIntf, PackIntf,
- {$LOAD}
- UMAUtil
- {$IFC qDebug}
- , UWritelnWindow
- , UTrace
- {$ENDC}
- ;
- {$ELSEC}
- USES
- MemTypes, QuickDraw, OSIntf, ToolIntf;
- {$ENDC UsingMacApp}
-
-
- {-----------------------------------+
- | Global types |
- +-----------------------------------}
- TYPE
- { used for the exception handling mechanism }
- PFailInfo = ^FailInfo;
- FailInfo = RECORD
- regs: ARRAY[1..11] OF LONGINT;
- error: INTEGER;
- message: LONGINT;
- failA6: LONGINT;
- failPC: LONGINT;
- nextInfo: PFailInfo;
- {this is needed for MacApp debugging}
- whoPC: LONGINT;
- {these are added for USignalFailure unit use}
- whatSignals: INTEGER;
- sigFRet: LONGINT; {used to keep the old stack frame return address}
- END;
-
-
- {-----------------------------------+
- | Global variables |
- +-----------------------------------}
- {$IFC UsingMacApp}
- {$IFC qDebug}
- VAR
- {$PUSH}
- {$Z+} {make ’em accessable to assembly code}
- gAskAboutAlloc: BOOLEAN; { if TRUE ask about each allocation to force
- failure }
- gAskFailure: BOOLEAN; { if TRUE ask about calls to
- FailOSErr, FailResError, FailMemError,
- and give user a chance to force error }
- {$POP}
- {$ENDC qDebug}
- {$ENDC UsingMacApp}
-
-
- {-----------------------------------+
- | Global routines |
- +-----------------------------------}
- {
- Call one of the two following initialization routines before your other
- initializations (InitGraf, etc.)--in other words as early as you can in the
- application.
- }
-
- PROCEDURE InitUFailure;
- { Allocates the heap block for CatchSignals and initializes the global
- variables used by the unit. No CatchSignals from the main level
- of Pascal are allowed if you use InitUFailure. [C programs must use this
- instead of InitSignals.] }
-
-
- PROCEDURE InitSignals;
- { Calls InitUFailure. It also sets up A6 at the main level of Pascal,
- so it must be called from the outermost level of Pascal. }
-
-
- FUNCTION CatchSignal: INTEGER;
- { Until the procedure which encloses this call returns, this will catch
- subsequent Signal calls, returning the code passed to Signal. When
- CatchSignal is encountered initially, it returns a code of zero. These
- calls may "nest"; i.e. you may have multiple CatchSignals in one procedure.
- If you signal with Failure and pass in a non-zero message you may want to use
- CatchFailures instead so you have a way of getting at the message. }
-
-
- PROCEDURE FreeSignal;
- { This undoes the effect of the last CatchSignal/CatchFailures. A Signal will
- then invoke the CatchSignal -prior- to the last one. }
-
-
- PROCEDURE Signal(code: INTEGER);
- { Returns control to the point of the last CatchSignal/CatchFailures. The program will
- then behave as though that CatchSignal had returned with this code parameter.
- If CatchFailures is catching, the message parameter will be 0. }
-
-
- PROCEDURE SignalMessage(code: INTEGER; message: LONGINT);
- { Returns control to the point of the last CatchSignal/CatchFailures.
- If CatchFailures is catching, the message parameter will be returned. }
-
-
- {-----------------------------------+
- | MacApp routines |
- +-----------------------------------}
-
-
-
- FUNCTION BuildMessage(lowWord, highWord: INTEGER): LONGINT;
- INLINE $2E9F; { MOVE.L (A7)+,(A7) }
- { Takes the 2 integers and combines them into a LONGINT. Note that the
- low-order word is the first parameter. }
-
- PROCEDURE CatchFailures(VAR fi: FailInfo;
- PROCEDURE Handler(e: INTEGER; m: LONGINT));
- { Call this to set up an exception handler. This pushes your handler onto
- a stack of exception handlers. }
-
- PROCEDURE Failure(error: INTEGER; message: LONGINT);
- { Call this to signal a failure. Control will branch to the most recent
- exception handler, which will be popped off the handler stack. }
-
- PROCEDURE FailMemError;
- { IF MemError <> noErr THEN Failure(MemError, 0); If you are using
- assembler, then you should just test the return code from the Memory
- Manager in DO by calling FailOSErr. (See the discussion of MemError in
- Inside Macintosh.) }
-
- PROCEDURE FailResError;
- { IF ResError <> noErr THEN Failure(ResError, 0); (See Inside Macintosh.) }
-
- PROCEDURE FailNewMessage(error: INTEGER; oldMessage, newMessage: LONGINT);
- { This does:
- IF oldMessage = 0 THEN
- Failure(error, newMessage)
- ELSE
- Failure(error, oldMessage);
- }
-
- PROCEDURE FailNIL(p: UNIV Ptr);
- { Call this with a pointer/handle; this signals Failure(memFullErr, 0) iff
- the pointer is NIL. }
-
- PROCEDURE FailOSErr(error: INTEGER);
- { Call this with an OSError; signals Failure(error, 0) iff error <> noErr. }
-
- {$IFC UsingMacApp}
- PROCEDURE SetInitHandler(handler: ProcPtr);
- { Sets an initialization error handler; applications should not call
- this, normally. }
- {$ENDC UsingMacApp}
-
- PROCEDURE Success(VAR fi: FailInfo);
- { Call this when you want to de-install your exception handler (pop 1
- element off the handler stack). }
-
- {$IFC UsingMacApp}
- {$IFC qDebug}
- PROCEDURE ProgramBreak(grievance: Str255);
- { Causes a program break to the debug window, displaying grievance. }
-
- PROCEDURE ProgramReport(grievance: Str255; break: BOOLEAN);
- { Displays grievance in the debug window, and breaks if break is true. }
- {$ENDC qDebug}
- {$ENDC UsingMacApp}
-
-
-
- IMPLEMENTATION
-
- {$I UFailure.inc1.p}
-
- END. {UFailure}
-